// 03.03.12 - Steven Hartin - Fixed constructors not initialising constants
// 01.03.12 - Matthew Brews - Original Creation.

#include "entity.h"

#include "mapcontainer.h"
#include "map.h"

CEntity::CEntity(const string &szKey, const ENTITY_ID &nId, float fX, float fY, 
                 float fZ, bool bVisible) : 
    m_szKey(szKey),
    m_nId(nId),
    m_fX(fX),
    m_fY(fY),
    m_fZ(fZ),
    m_bVisible(bVisible),
    m_pDisplayable(nullptr),
    m_bDirtyBit(false),
    m_bPermanent(false),
    m_bCollectible(false),
    m_nSubId(ENT_NONE)
{
    /**
     * @param const string &szKey The string key reference of the entity
     * @param const ENTITY_ID &nId The ID of the entity
     * @param float x The X position of the entity
     * @param float y The Y position of the entity
     * @param float z The z position of the entity
     * @param bool visible The initial visibility state of the entity.
     */
}

CEntity::CEntity(const ENTITY_ID &nId, float fX, float fY, float fZ, 
        bool bVisible):
    m_szKey(""),
    m_nId(nId),
    m_fX(fX),
    m_fY(fY),
    m_fZ(fZ),
    m_bVisible(bVisible),
    m_pDisplayable(nullptr),
    m_bDirtyBit(false),
    m_bPermanent(false),
    m_bCollectible(false),
    m_nSubId(ENT_NONE)
{
    /**
     * @param const ENTITY_ID &nId The ID of the entity
     * @param float x The X position of the entity
     * @param float y The Y position of the entity
     * @param float z The z position of the entity
     * @param bool visible The initial visibility state of the entity.
     */
}

CEntity::~CEntity()
{
    /**
     * Destructor, if the entity owns a displayable object then remove it from
     * the list.
     */
    if (getSubID() != ENT_TILE_NONE)
    {
        CMapContainer *pMaps(CMapContainer::getInstance());
        if (pMaps->isMapLoaded())
        {
            pMaps->getCurrentMap()->deleteEntityFromTile(this);
        }
    }
    if (m_pDisplayable)
    {
        CDisplayableContainer::getInstance()->removeDisplayable(
            m_pDisplayable);
    }
}

bool CEntity::isCollectible() const
{
    /**
     * @return bool Whether or not the entity can be collected
     */
    return m_bCollectible;
}

void CEntity::setCollectible(bool bCollectible)
{
    /**
     * Sets the collectible state to a different value
     *
     * @param bool bCollectable Whether or not the entity can now be collected
     */
    m_bCollectible = bCollectible;
}

bool CEntity::getVisible() const
{
    /**
     * @return bool Whether or not the entity is visible
     */
    return m_pDisplayable->isVisible();
}

void CEntity::setVisible(bool bVisible)
{
    /**
     * Sets the visible state to a new value.
     *
     * @param bool bVisible The visibility state of the entity
     */
    if (m_pDisplayable != nullptr)
    {
        m_pDisplayable->setVisible(bVisible);
    }
}

void CEntity::destroy()
{
    /**
     * Executed when an entity is destroyed.
     */
}

CDisplayable *CEntity::getDisplayable() const
{
    /**
     * @return CDisplayable* The displayable object for the entity
     */
    return m_pDisplayable;
}

void CEntity::setDisplayable(CDisplayable *pDisplayable)
{
    /**
     * Sets the displayable object to another displayable object.
     *
     * @param CDisplayable* A pointer to the new displayable object.
     */
    m_pDisplayable = pDisplayable;
    if (m_pDisplayable != nullptr)
    {
        m_pDisplayable->setTranslate(m_fX, m_fY, m_fZ);
        m_pDisplayable->setVisible(m_bVisible);
    }
}

string CEntity::getKey() const
{
    /**
     * @return string The string key representation of the entity
     */
    return m_szKey;
}

ENTITY_ID CEntity::getID() const 
{
    /**
     * @return ENTITY_ID The ID of the entity
     */
    return m_nId;
}

ENTITY_ID CEntity::getSubID() const
{
    /**
     * @return ENTITY_ID Any Sub ID of the entity
     */
    return m_nSubId;
}

void CEntity::setSubID(const ENTITY_ID &nId)
{
    /**
     * @param ENTITY_ID &nId The new sub ID to assign to the entity
     */
    m_nSubId = nId;
}

void CEntity::render() const 
{
    /**
     * Renders the entity. This just renders the displayable object if it is
     * visible.
     */
    if (m_pDisplayable != nullptr)
    {
        if (getVisible())
        {
            m_pDisplayable->render();
        }
    }
}

bool CEntity::isPermanent() const
{
    /**
     * @return bool Whether or not the entity is permanent (should persist
     *    after a reset).
     */
    return m_bPermanent;
}

void CEntity::setPermanent(bool bPermanent)
{
    /**
     * Sets the permanent state of the entity to a new value. A permanent state
     * specifies whether the entity will stay after a map reset.
     *
     * @param bool bPermanent Whether or not the entity is permanent
     */
    m_bPermanent = bPermanent;
}

void CEntity::setPosition(float fX, float fY, float fZ)
{
    /**
     * Sets the position of the entity to a new position. This also updates the
     * position of any attached entities.
     *
     * @param float fX The new X position of the entity
     * @param float fY The new Y position of the entity
     * @param float fZ The new Z position of the entity
     */
    m_fX = fX;
    m_fY = fY;
    m_fZ = fZ;

    if (m_pDisplayable != nullptr)
    {
        // Update the position of the displayable object
        m_pDisplayable->setTranslate(m_fX, m_fY, m_fZ);
    }

    for (vector<st_attatchable>::iterator pAttachable(
         m_arrAttachments.begin()); pAttachable != m_arrAttachments.end();
         ++pAttachable)
    {
        // For each attachable, update the position.
        pAttachable->pEntity->setPosition(fX + pAttachable->fXOffset,
                                           fY + pAttachable->fYOffset,
                                           fZ + pAttachable->fZOffset);
    }
}

void CEntity::setX(float fX)
{
    /**
     * Sets the X position of the entity and any attachable to it's new X
     * position.
     *
     * @param float fX The X position of the entity
     */
    m_fX = fX;
    
    if (m_pDisplayable != nullptr)
    {
        // Update the position of the displayable object
        m_pDisplayable->setXTranslate(m_fX);
    }

    for (vector<st_attatchable>::iterator pAttachable(
         m_arrAttachments.begin()); pAttachable != m_arrAttachments.end();
         ++pAttachable)
    {
        // For each attachable, update the position.
        pAttachable->pEntity->setX(fX + pAttachable->fXOffset);
    }
}

void CEntity::setY(float fY)
{
    /**
     * Sets the Y position of the entity and any attachable to it's new Y
     * position.
     *
     * @param float fY The Y position of the entity
     */
    m_fY = fY;
    
    if (m_pDisplayable != nullptr)
    {
        // Update the position of the displayable object
        m_pDisplayable->setYTranslate(m_fY);
    }
    for (vector<st_attatchable>::iterator pAttachable(
         m_arrAttachments.begin()); pAttachable != m_arrAttachments.end();
         ++pAttachable)
    {
        // For each attachable, update the position.
        pAttachable->pEntity->setY(fY + pAttachable->fYOffset);
    }
}

void CEntity::setZ(float fZ)
{
    /**
     * Sets the Z position of the entity and any attachable to it's new Z
     * position.
     *
     * @param float fZ The Z position of the entity
     */
    m_fZ = fZ;

    if (m_pDisplayable != nullptr)
    {
        // Update the position of the displayable object
        m_pDisplayable->setZTranslate(m_fZ);
    }
    for (vector<st_attatchable>::iterator pAttachable(
         m_arrAttachments.begin()); pAttachable != m_arrAttachments.end();
         ++pAttachable)
    {
        // For each attachable, update the position.
        pAttachable->pEntity->setZ(fZ + pAttachable->fZOffset);
    }
}

void CEntity::getPosition(float &fX, float &fY, float &fZ)
{
    /**
     * Obtains the position and stores them in the values passed in.
     *
     * @param float &fX The variable to store the X position.
     * @param float &fY The variable to store the Y position.
     * @param float &fZ The variable to store the Z position.
     */
    fX = m_fX;
    fY = m_fY;
    fZ = m_fZ;
}

float CEntity::getX() const
{
    /**
     * @return float The X position of the entity
     */
    return m_fX;
}

float CEntity::getY() const
{
    /**
     * @return float The Y position of the entity
     */
    return m_fY;
}

float CEntity::getZ() const
{
    /**
     * @return float The Z position of the entity
     */
    return m_fZ;
}

CEntity *CEntity::getOwner() const
{
    /**
     * @return CEntity* The entity which owns this entity.
     */
    return m_pOwner;
}

void CEntity::setOwner(CEntity *pEntity)
{
    /**
     * @param CEntity *pEntity The entity to own this entity.
     */
    m_pOwner = pEntity;
}

void CEntity::attachEntity(CEntity *pEntity, float fXOffset, float fYOffset,
                            float fZOffset)
{
    /**
     * Attaches an entity to this entity. Attached entities follow each other
     * around, whenever the parent is moved, the children are also moved to
     * the same position minus any offsets given.
     *
     * @param CEntity *pEntity The entity to attach
     * @param float fXOffset The X offset from the parent
     * @param float fYOffset The Y offset from the parent
     * @param float fZOffset The Z offset from the parent
     */
    st_attatchable attatchable = {pEntity, fXOffset, fYOffset, fZOffset};
    m_arrAttachments.push_back(attatchable);
    pEntity->setOwner(this);
}

void CEntity::detachEntity(CEntity *pEntity)
{
    /**
     * Detaches the entity from this entity. Sets the entities owners to
     * null pointers.
     *
     * @param CEntity *pEntity The entity to detach from.
     */
    for (vector<st_attatchable>::const_iterator pPosition(
            m_arrAttachments.begin()); pPosition != m_arrAttachments.end(); 
            ++pPosition)
    {
        if (pPosition->pEntity == pEntity)
        {
            pPosition->pEntity->setOwner(nullptr);
            m_arrAttachments.erase(pPosition);
            return;
        }
    }
}

bool CEntity::isAttached(CEntity *pEntity)
{
    /**
     * @param CEntity *pEntity The entity to check if it is attached
     * @return bool Whether the entity is attached
     */
    for (vector<st_attatchable>::const_iterator pPosition(
         m_arrAttachments.begin()); pPosition != m_arrAttachments.end(); 
         ++pPosition)
    {
        if (pPosition->pEntity == pEntity)
        {
            return true;
        }
    }
    return false;
}

void CEntity::setDirtyBit()
{
    /**
     * Assigns the dirty bit (deleted on next iteration) to true.
     */
    m_bDirtyBit = true;
}

bool CEntity::getDirtyBit()
{
    /**
     * @return bool Whether or not the entity is set to be destroyed on the
     *    next iteration.
     */
    return m_bDirtyBit;
}